package qf.index;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReadKw {

    public static void main(String[] args) throws SQLException {

        /*
          读取mesh表，保存进keyWordAndMeshMap（此map中的款目词作了标准化处理，参与截词法提取关键词）和originalAndMeshMap（此map中的款目词仅是转换成了小写），并将上一步中获取的关键词及数量（keyWordSumMap）进行插表（keyWordSumTable）。
          注意事项：
          1. 关键词出现多少次才可以认定为关键词呢？在测试程序中大于0即可，实际使用时，以大于50次为标准，设置在方法：insertKeyWordSumMap中（if (sum > 0 || (keyWordList.size() == 2 && sum > 0) || (keyWordList.size() == 3 && sum > 0)) {），
          2. keyWordList.size() == 2即是全写与缩写，数量可以设置为10次。keyWordList.size() == 3即是mesh词，数量可以设置为1次。
         */

        String newDataName = "scholar_25_01.";
        String summaryTable = newDataName + "pm_ut_aid_info_0120";
        String spiderCnCoreTable = newDataName + "spider_cn_core";
        String spiderCnKiAnalysisTable = newDataName + "spider_cnki_analysis";
        String spiderCqVipAnalysisTable = newDataName + "spider_cqvip_analysis";
        String spiderWanFangAnalysisTable = newDataName + "spider_wanfang_analysis";
        String spiderYiiGleAnalysisTable = newDataName + "spider_yiigle_analysis";
        String pmArticleTable = newDataName + "spider_pubmed_vip_extend";
        String utArticleTable = newDataName + "spider_tb_true_92007022_1125";
        String jourMergeTable = newDataName + "basic_jour_merge";
        String quotaTable = newDataName + "basic_core_paper_quota";
        String basicCoreTable = newDataName + "basic_core_paper";
        String wordAndCodeSetMapTable = newDataName + "word_and_code_set_map";
        String fullAbbrKeyWordTable = newDataName + "full_abbr_key_word_0124";
        String basicDeptTable = "scholar_25_01.tb_true_china_hosp_6_year_dept";
        String keyWordSumTable = newDataName + "key_word_sum_0124";
        String meshTable = newDataName + "help_mesh_tree_gcr_0615";
        String cnBasicDeptTable = newDataName + "cn_dept_sum_dy30";
        String basicUnivCollTable = newDataName + "basic_univ_coll";
        String htLocationChinaTable = newDataName + "ht_location_china";
        String basicHospUnivDeptTable = newDataName + "basic_hosp_or_univ_dept";
        String hospUnivInfoSumTable = newDataName + "hosp_or_univ_info_sum";

        int onePageNum = 1000;
        String limit = "";
//        String limit = " limit 1";

        readKeyWord(summaryTable, onePageNum, limit, "id", "title`,`keyword`,`ab`,`cn_kw`,`cn_ab`,`aid`,`pmId`,`ut`,`c1_au`,`rp_au`,`language", true, 10, fullAbbrKeyWordTable, keyWordSumTable,
                meshTable, htLocationChinaTable, basicHospUnivDeptTable, hospUnivInfoSumTable);

    }

    public static void readKeyWord(String summaryTable, int onePageNum, String limit, String autoId, String field, boolean print, int allTotal, String fullAbbrKeyWordTable,
                                   String keyWordSumTable, String meshTable, String htLocationChinaTable, String basicHospUnivDeptTable, String hospUnivInfoSumTable) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + summaryTable);
        if (tableNum.get(0).getInt("count") < 1) {
            return;
        }

        Db.use().execute("truncate table " + fullAbbrKeyWordTable);
        Db.use().execute("truncate table " + keyWordSumTable);
        Map<String, String> languageMap = uSet.getLanguageMap();
        Map<String, String> reKwMap = cList.getReKwMap();
        Map<List<String>, Integer> keyWordSumMap = new ConcurrentHashMap<>();
        Map<String, Integer> cnKeyWordSumMap = new ConcurrentHashMap<>();
        Map<String, String> md5Map = new ConcurrentHashMap<>();
        Map<String, String> keyWordAndMeshMap = new ConcurrentHashMap<>();
        Map<String, String> originalAndMeshMap = new ConcurrentHashMap<>();
        int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + summaryTable).intValue();
        Collection<Integer> tableList = new ArrayList<>();
        NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
        AtomicInteger sumNum = new AtomicInteger(0);
        int showNum = Math.max((tableList.size() / allTotal), 1) + (tableList.size() % allTotal);
        AtomicInteger startNum = new AtomicInteger(1);
        List<String> originalWordList = rSet.getWordList(); // 共1673个，先长的后短的，区分大小写。
        Set<String> smallWordSet = rSet.getSimpWordSet();
        Set<String> backgroundSet = rSet.toLowerCaseSet(rSet.getBackgroundSet());
        Set<String> discussionSet = rSet.toLowerCaseSet(rSet.getDiscussionSet());
        Set<String> methodsSet = rSet.toLowerCaseSet(rSet.getMethodsSet());
        Set<String> resultsSet = rSet.toLowerCaseSet(rSet.getResultsSet());
        Set<String> chinaSet = cList.chinaSet();
        Set<String> citySet = new HashSet<>();
        Set<String> subjectSet = cList.getSubjectSet();
        Set<String> provCitySet = Dept.getProvCitySet(htLocationChinaTable, "id", "prov`,`name", citySet);
        readMesh(meshTable, "id", "meshName`,`entryTerms", true, 10, md5Map, keyWordAndMeshMap, originalAndMeshMap, reKwMap);
        Map<String, String> standardMap = new ConcurrentHashMap<>();
        standardMap.put("department medical", "Department Medical");
        Map<String, Set<String>> deptAndInfoSetMap = new ConcurrentHashMap<>();
        Map<String, Integer> deptAndSumMap = new ConcurrentHashMap<>();
        Map<String, Set<String>> unitAndInfoSetMap = new ConcurrentHashMap<>();
        Map<String, Integer> unitAndSumMap = new ConcurrentHashMap<>();
        Map<String, String> countryMap = cList.getCountryMap();
        Set<String> acadSet = uSet.getAcadSet();
        Set<String> centerSet = uSet.getCenterSet();
        Set<String> collSet = uSet.getCollSet();
        Set<String> hospSet = uSet.getHospSet();
        Set<String> univSet = uSet.getUnivSet();
        Set<String> removeSet = uSet.getRemoveSet();
        Set<String> dellSet = uSet.getDellSet();
        Set<String> toDeptSet = uSet.toDept();
        Map<String, En.FuDanStemEdu> fuDanStemEduMap = Guidelines.getFuDanStemEduMap(basicHospUnivDeptTable, "id", "info`,`fudan`,`stem`,`edu");

        tableList.parallelStream().forEach(startId -> {
            try {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + summaryTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
                if (print && sumNum.getAndIncrement() % showNum == 0) {
                    System.out.println("\033[31;1m" + format.format(new Date()) + " ⑼ 更新作者，现在开始：" + startNum.getAndIncrement() + " 总次数：" + allTotal + " 表名：" + summaryTable + Utils.printMemoryStr() + "\033[0m");
                }
                for (Entity entry : tableData) {
                    Integer id = entry.getInt(autoId);
                    if (null == id || id < 1) {
                        continue;
                    }
                    Set<List<String>> abbSet = new HashSet<>();
                    String title = Utils.getStrField(entry, "title");
                    String keyword = Utils.getStrField(entry, "keyword");
                    String ab = Utils.getStrField(entry, "ab");
                    String cnKw = Utils.getStrField(entry, "cn_kw");
                    String cnAb = Utils.getStrField(entry, "cn_ab");
                    int aid = Utils.getIntField(entry, "aid");
                    int pmId = Utils.getIntField(entry, "pmId");
                    String ut = Utils.getStrField(entry, "ut");
                    List<String> keywordList = (keyword.contains("\"]")) ? JSONObject.parseArray(keyword, String.class) : new ArrayList<>();
                    addKeySet(CollUtil.join(keywordList, "; "), new HashSet<>(), keyWordSumMap, cnKeyWordSumMap);
                    getAbbrKeyWord(title, fullAbbrKeyWordTable, aid, abbSet, pmId, ut, keyWordSumMap);
                    List<En.Trans> transList = transAbstract(ab, originalWordList, smallWordSet);
                    En.AbKeyMethod abKeyMethod = getTransStr(backgroundSet, discussionSet, methodsSet, resultsSet, transList); // 这里面是四个
                    // replaceAll(":([ ]{0,1})([0-9.]+)", "：$1$2")，英文的冒号，变成中文的冒号，比如：value: 123.45 -> value：123.45
                    ab = ab.replace("：", ": ").replace("。", ". ").replaceAll(" +", " ").trim().replaceAll(":([ ]{0,1})([0-9.]+)", "：$1$2");
                    getAbbrKeyWord(ab, fullAbbrKeyWordTable, aid, abbSet, pmId, ut, keyWordSumMap);

                    List<String> cnKeyList = (cnKw.contains("\"]")) ? JSONObject.parseArray(cnKw, String.class) : new ArrayList<>();
                    addKeySet(CollUtil.join(cnKeyList, "; "), new HashSet<>(), keyWordSumMap, cnKeyWordSumMap);
                    getCnAbbrKeyWord(cnAb, fullAbbrKeyWordTable, aid, abbSet, pmId, ut, keyWordSumMap);
                    En.AbKeyMethod cnAbKeyMethod = getCnAbKeyMethod(cnAb);

                    String c1Au = Utils.getStrField(entry, "c1_au");
                    String rpAu = Utils.getStrField(entry, "rp_au");
                    List<En.C1Auth> c1List = (c1Au.contains("sort")) ? JSONObject.parseArray(c1Au, En.C1Auth.class) : new ArrayList<>();
                    List<En.C1Auth> rpChangeC1 = (rpAu.contains("sort")) ? JSONObject.parseArray(rpAu, En.C1Auth.class) : new ArrayList<>();
                    Guidelines.setC1List(c1List, standardMap, acadSet, centerSet, collSet, hospSet, univSet, removeSet, countryMap, deptAndSumMap, dellSet,
                            chinaSet, provCitySet, citySet, subjectSet, deptAndInfoSetMap, unitAndInfoSetMap, unitAndSumMap, toDeptSet, fuDanStemEduMap);
                    Guidelines.setC1List(rpChangeC1, standardMap, acadSet, centerSet, collSet, hospSet, univSet, removeSet, countryMap, deptAndSumMap, dellSet,
                            chinaSet, provCitySet, citySet, subjectSet, deptAndInfoSetMap, unitAndInfoSetMap, unitAndSumMap, toDeptSet, fuDanStemEduMap);
                    String language = Utils.getStrField(entry, "language");
                    String[] split = language.replace(";-;", "ĺļ").replace("; ", "ĺļ").replace(";", "ĺļ").split("ĺļ");
                    List<String> languageList = new ArrayList<>();
                    for (String s : split) {
                        if (s.trim().length() > 2) {
                            languageList.add(languageMap.getOrDefault(s.toLowerCase().trim(), s.trim()));
                        }
                    }
                    try {
                        Db.use().update(
                                Entity.create().set("discussion", abKeyMethod.getDiscussion())
                                        .set("language", CollUtil.join(languageList, "; "))
                                        .set("results", abKeyMethod.getResults())
                                        .set("methods", abKeyMethod.getMethods())
                                        .set("background", abKeyMethod.getBackground())
                                        .set("abb_word", JSONUtil.toJsonStr(abbSet))
                                        .set("c1_au", JSONUtil.toJsonStr(c1List))
                                        .set("rp_au", JSONUtil.toJsonStr(rpChangeC1))
                                        .set("cn_discussion", cnAbKeyMethod.getDiscussion())
                                        .set("cn_results", cnAbKeyMethod.getResults())
                                        .set("cn_methods", cnAbKeyMethod.getMethods())
                                        .set("cn_background", cnAbKeyMethod.getBackground()),
                                Entity.create(summaryTable).set(autoId, id));
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });

        // 将科室信息，包含医院、大学的统一给保存进basicHospUnivDeptTable表，可以在这一张表中处理复旦、STEM和教育部学科。此不需要每次都弄，除非要换大模型，否则可以一直使用已经规范的。
//        Set<String> set = new HashSet<>();
//        for (Map.Entry<String, Integer> entry : deptAndSumMap.entrySet()) {
//            if (entry.getKey().startsWith("[univ]") || entry.getKey().startsWith("[hosp]")) {
//                String dept = entry.getKey().substring("[univ]".length());
//                if (dept.length() > 1) {
//                    set.add(dept);
//                }
//            }
//        }
//        Db.use().execute("truncate table " + basicHospUnivDeptTable);
//        set.parallelStream().forEach(dept -> {
//            Set<String> univAdnInfoSet = (deptAndInfoSetMap.containsKey("[univ]" + dept)) ? deptAndInfoSetMap.get("[univ]" + dept) : new HashSet<>();
//            Set<String> hospAdnInfoSet = (deptAndInfoSetMap.containsKey("[hosp]" + dept)) ? deptAndInfoSetMap.get("[hosp]" + dept) : new HashSet<>();
//            int univSum = deptAndSumMap.getOrDefault("[univ]" + dept, 0);
//            int hospSum = deptAndSumMap.getOrDefault("[hosp]" + dept, 0);
//            try {
//                Db.use().insert(Entity.create(basicHospUnivDeptTable).set("info", dept).set("univ_c1s", JSONUtil.toJsonStr(univAdnInfoSet)).set("hosp_c1s", JSONUtil.toJsonStr(hospAdnInfoSet))
//                        .set("univ_sum", univSum).set("hosp_sum", hospSum).set("is_univ", univSum > 0 ? 1 : 0).set("is_hosp", hospSum > 0 ? 1 : 0));
//            } catch (SQLException e) {
//                e.printStackTrace();
//            }
//        });
//
//        // 需要规范的单位的信息
//        unitAndInfoSetMap.keySet().parallelStream().forEach(unit -> {
//            try {
//                Db.use().insert(Entity.create(hospUnivInfoSumTable).set("unit", unit).set("example", JSONUtil.toJsonStr(unitAndInfoSetMap.get(unit))).set("sum", unitAndSumMap.getOrDefault(unit, 0)));
//            } catch (SQLException e) {
//                e.printStackTrace();
//            }
//        });

        languageMap.clear();
        originalWordList.clear();
        smallWordSet.clear();
        backgroundSet.clear();
        discussionSet.clear();
        methodsSet.clear();
        resultsSet.clear();
        chinaSet.clear();
        citySet.clear();
        subjectSet.clear();
        provCitySet.clear();
        standardMap.clear();
        deptAndInfoSetMap.clear();
        deptAndSumMap.clear();
        unitAndInfoSetMap.clear();
        unitAndSumMap.clear();
        countryMap.clear();
        acadSet.clear();
        centerSet.clear();
        collSet.clear();
        hospSet.clear();
        univSet.clear();
        removeSet.clear();
        dellSet.clear();
        toDeptSet.clear();
        fuDanStemEduMap.clear();
        Map<String, String> keyWordAndStandardMap = insertKeyWordSumMap(keyWordSumTable, keyWordSumMap, keyWordAndMeshMap, md5Map, cnKeyWordSumMap, reKwMap);
        keyWordSumMap.clear();
        keyWordAndMeshMap.clear();
        md5Map.clear();
        cnKeyWordSumMap.clear();
        reKwMap.clear();
        updateKeyWord(summaryTable, onePageNum, limit, "id", "keyword`,`title`,`abb_word`,`ab`,`cn_kw`,`cn_ti`,`cn_ab", true, 10, originalAndMeshMap, keyWordAndStandardMap);
        originalAndMeshMap.clear();
        keyWordAndStandardMap.clear();
    }

    public static void updateKeyWord(String summaryTable, int onePageNum, String limit, String autoId, String field, boolean print, int allTotal, Map<String, String> originalAndMeshMap, Map<String, String> keyWordAndStandardMap) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + summaryTable);
        if (tableNum.get(0).getInt("count") < 1) {
            return;
        }
        int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + summaryTable).intValue();
        Collection<Integer> tableList = new ArrayList<>();
        NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
        AtomicInteger sumNum = new AtomicInteger(0);
        int showNum = Math.max((tableList.size() / allTotal), 1) + (tableList.size() % allTotal);
        AtomicInteger startNum = new AtomicInteger(1);
        tableList.parallelStream().forEach(startId -> {
            try {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + summaryTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
                if (print && sumNum.getAndIncrement() % showNum == 0) {
                    System.out.println("\033[31;1m" + format.format(new Date()) + " ⑽ 更新关键词，现在开始：" + startNum.getAndIncrement() + " 总次数：" + allTotal + " 表名：" + summaryTable + Utils.printMemoryStr() + "\033[0m");
                }
                for (Entity entry : tableData) {
                    Integer aid = entry.getInt(autoId);
                    if (null == aid || aid < 1) {
                        continue;
                    }
                    String keywords = entry.getStr("keyword");
                    Set<List<String>> kwSetList = new HashSet<>();
                    if (null != keywords && keywords.contains("\"]")) {
                        List<String> kwList = JSONObject.parseArray(keywords, String.class);
                        addSetList(originalAndMeshMap, kwList, kwSetList, false);
                    }

                    String title = entry.getStr("title");
                    Set<List<String>> tiSetList = new HashSet<>();
                    Set<String> tiKeySet = (null != title && title.length() > 4) ? getTiKey(title, keyWordAndStandardMap) : new HashSet<>();
                    addSetList(originalAndMeshMap, new ArrayList<>(tiKeySet), tiSetList, true);

                    Set<List<String>> abSetList = new HashSet<>();
                    String abbWord = entry.getStr("abb_word");
                    List<String> abbWordList = (null != abbWord && abbWord.contains("\"]")) ? JSONObject.parseArray(abbWord, String.class) : new ArrayList<>();
                    for (String lists : abbWordList) {
                        List<String> list = JSONObject.parseArray(lists, String.class);
                        abSetList.add(list);
                    }

                    String ab = entry.getStr("ab");
                    Set<String> abKeySet = (null != ab && ab.length() > 10) ? getAbKey(ab, keyWordAndStandardMap) : new HashSet<>();
                    Set<String> abSet = new HashSet<>();
                    for (String abKey : abKeySet) {
                        boolean bool = true;
                        for (List<String> list : abSetList) {
                            if (list.contains(abKey)) {
                                bool = false;
                                break;
                            }
                        }
                        if (bool) {
                            abSet.add(abKey);
                        }
                    }
                    addSetList(originalAndMeshMap, new ArrayList<>(abSet), abSetList, true);

                    // 关键词基本上都是保持原有的样子不作改变，而从标题和摘要的，则要使用提取的程序。
                    String cnKeywords = entry.getStr("cn_kw");
                    if (null != cnKeywords && cnKeywords.contains("\"]")) {
                        List<String> kwList = JSONObject.parseArray(cnKeywords, String.class);
                        setToSetList(new HashSet<>(kwList), kwSetList);
                    }

                    String cnTi = entry.getStr("cn_ti");
                    Set<String> cnTiKeySet = (null != cnTi && cnTi.length() > 4) ? getCnTiKey(cnTi, keyWordAndStandardMap) : new HashSet<>();
                    setToSetList(cnTiKeySet, tiSetList);

                    String cnAb = entry.getStr("cn_ab");
                    Set<String> cnAbKeySet = (null != cnAb && cnAb.length() > 10) ? getCnAbKey(cnAb, keyWordAndStandardMap) : new HashSet<>();
                    setToSetList(cnAbKeySet, abSetList);

                    Set<String> allSet = new HashSet<>();
                    addAllSet(tiSetList, allSet);
                    addAllSet(kwSetList, allSet);
                    addAllSet(abSetList, allSet);
                    try {
                        Db.use().update(
                                Entity.create().set("ti_key", JSONUtil.toJsonStr(tiSetList))
                                        .set("kw_key", JSONUtil.toJsonStr(kwSetList))
                                        .set("ab_key", JSONUtil.toJsonStr(abSetList))
                                        .set("key_word_list", JSONUtil.toJsonStr(allSet))
                                , Entity.create(summaryTable).set(autoId, aid));
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
    }

    public static Set<String> getCnAbKey(String abs, Map<String, String> keyWordAndStandardMap) {
        Set<String> abSet = new HashSet<>();
        if (null != abs && abs.length() > 1) {
            // 英文的点后面，如果是中文或字母给替换为空格，如果是数字则仍保留这个点，意思是0.05是不能切割的。
            String ab = abs.replace("；", " ").replace("，", ",").replace("。", " ").replace("、", " ").replace("（", " ").replace("）", " ").replace("【", " ")
                    .replace("：", " ").replace("！", " ").replace("？", " ").replace(";", " ").replaceAll("\\.([\u4e00-\u9fa5a-zA-Z])", " $1").replace("(", " ").replace("】", " ")
                    .replace(")", " ").replace(":", " ").replace("!", " ").replace("?", " ").replace(" ,", ",").replace(", ", ",").replaceAll(" +", " ").trim();
            String[] split = ab.split(" ");
            for (String s : split) {
                if (s.trim().length() > 4) {
                    extract(s, keyWordAndStandardMap, abSet);
                }
            }
        }
        return abSet;
    }

    // 从摘要中提取关键词，先以句号切割，再一句句提取。
    public static Set<String> getAbKey(String abs, Map<String, String> keyWordAndStandardMap) {
        String ab = abs.replace("? ", ". ").replace("; ", ". ").replace(": ", ". ").trim();
        Set<String> abSet = new HashSet<>();
        if (ab.length() > 11 && ab.contains(". ")) {
            ab = ab.replace("<p>", " ").replace("</p>", " ").replace("  ", " ").trim();
            ab = ab.endsWith(".") ? ab.substring(0, ab.length() - 1).trim() : ab.trim();
            String[] split = ab.split("\\. ");
            for (String s : split) {
                s = s.trim();
                s = s.endsWith(".") ? s.substring(0, s.length() - 1) : s;
                if (s.length() > 4 && s.contains(" ")) {
                    getBlanket(s, abSet);
                    extract(s, keyWordAndStandardMap, abSet);
                }
            }
        }
        return abSet;
    }

    // 从标题中提取关键词，部分标题中存在方括号，先给予去除。
    public static Set<String> getCnTiKey(String ti, Map<String, String> keyWordAndStandardMap) {
        Set<String> titleSet = new HashSet<>();
        if (null != ti && ti.length() > 4) {
            extract(ti, keyWordAndStandardMap, titleSet);
        }
        return titleSet;
    }

    // 从标题中提取关键词，部分标题中存在方括号，先给予去除。
    public static Set<String> getTiKey(String title, Map<String, String> keyWordAndStandardMap) {
        Set<String> titleSet = new HashSet<>();
        String ti = title.trim();
        if (ti.length() > 4 && ti.contains(" ")) {
            ti = ti.startsWith("[") ? ti.substring(1).trim() : ti.trim();
            ti = ti.endsWith(".") ? ti.substring(0, ti.length() - 1).trim() : ti.trim();
            ti = ti.endsWith("]") ? ti.substring(0, ti.length() - 1).trim() : ti.trim();
            ti = ti.endsWith(".") ? ti.substring(0, ti.length() - 1).trim() : ti.trim();
            getBlanket(ti, titleSet);
            extract(ti, keyWordAndStandardMap, titleSet);
        }
        return titleSet;
    }

    // 提取hepatocyte growth factor (HGF)->hepatocyte growth factor
    public static void getBlanket(String bracket, Set<String> abSet) {
        if (bracket.contains("(") && bracket.contains(")")) {
            bracket = bracket.replace("(", " (").replace(")", ") ").replace("-", "㵝纃 ").replaceAll(" +", " ").replace("( ", "(").replace(" )", ")").toLowerCase().trim();
            String[] list = bracket.split(" ");
            for (int i = 0; i < list.length; i++) {
                String s = list[i];
                if (s.startsWith("(") && s.endsWith(")") && s.length() > 3) {
                    String t = s.substring(1, s.length() - 1);
                    String[] split = t.split("");
                    if (i >= split.length) {
                        StringBuilder result = new StringBuilder();
                        boolean bool = true;
                        for (int j = 0; j < split.length; j++) {
                            if (list[i - split.length + j].startsWith(split[j])) {
                                result.append(list[i - split.length + j]).append(" ");
                            } else {
                                bool = false;
                            }
                        }
                        String r = result.toString().trim();
                        if (bool && r.contains(" ") && r.length() > 2) {
                            abSet.add(r.replace("㵝纃 ", "-").replace("㵝纃", "-"));
                        }
                    }
                }
            }
        }
    }

    // 执行提取，输入自由文本freeWords，标准词Map，取回提取到的集合extractSet，以及提取后的字符串remaining
    public static void extract(String freeWords, Map<String, String> keyWordAndStandardMap, Set<String> extractSet) {
//        System.out.println("freeWords_0:" + freeWords);
        StringBuilder remaining = new StringBuilder();
        if (null != freeWords && freeWords.length() > 1) {
            freeWords = reRemaining(freeWords).replaceAll("( non )", " non-").replaceAll("(^non )", "non-")
                    .replaceAll("( not )", " not-").replaceAll("(^not )", "not-")
                    .replace("非", "非-").replace("不", "不-").replaceAll("(.?-.?)", "㵝$1㵝");
            String spl = (Utils.isContainChinese(freeWords.replace("纃", "").replace("㵝", ""))) ? "" : " ";
            int splLen = spl.length();
            List<String> list = Arrays.asList(freeWords.split(spl));
            int iLen = 0;
            int listLen = list.size();
            for (int i = 0; i < listLen; i++) {
                if (freeWords.length() >= iLen) {
                    String iStr = freeWords.substring(iLen);
//                    System.out.println("iStr: " + iStr);
                    iLen += list.get(i).length() + splLen;
                    int jLen = 0;
                    for (int j = listLen - 1; j >= 0; j--) {
                        if (iStr.length() - jLen >= 0) {
                            String jStr = iStr.substring(0, iStr.length() - jLen);
//                            System.out.println("jStr: " + jStr);
                            jLen += list.get(j).length() + splLen;
                            String md5Name = (jStr.length() > 16) ? SecureUtil.md5(jStr).substring(8, 24).toLowerCase() : "";
                            if (keyWordAndStandardMap.containsKey(jStr) || (!md5Name.isEmpty() && keyWordAndStandardMap.containsKey(md5Name))) {
                                if ((Utils.isContainChinese(jStr) && extractChinese(freeWords, jStr)) || (!Utils.isContainChinese(jStr) && extractEnglish(freeWords, jStr))) {
                                    String mapValue = (jStr.length() > 16) ? keyWordAndStandardMap.get(md5Name) : keyWordAndStandardMap.get(jStr);
                                    String standardName = getStandardName(mapValue, jStr).replace("non-", "non ").replace("not-", "not ").replace("非-", "非").replace("不-", "不");
//                                    String original = resetRemaining(jStr.replace("㵝", ""));
                                    String s = resetRemaining(standardName);
                                    if (s.length() > 1) {
                                        extractSet.add(s);
                                    }
//                                    extractSet.add(original); // 上面的是标准的名称，下面的是原始的名称，可以只拿标准的，不拿原始的。
                                    iLen += (jStr.length() - list.get(i).length());
                                    i = i + jStr.split(spl).length - 1;
                                    break;
                                }
                            }
//                            System.out.println("~~" + jStr);
                            if (jStr.split(spl).length == 1) {
//                                System.out.println("--" + jStr + " freeWords:" + freeWords + " remaining:" + remaining);
//                                remaining.toString().substring(0,remaining.toString().length()-1)
                                remaining.append(jStr).append(spl);
                            }
                        }
                    }
                }
            }
        }

        // 下面的代码功能是，如果连续的两个字符在原字符串中，则不添加空格，若在则添加空格。比如：中华人民共和国万岁毛泽东万岁贾采用免疫组化SP法检测35例CA组织中STAT3和VEGF的表达新非小细胞大细胞 留下：贾 新 非 小 细，处理后返回：贾 新非小细
//        String f = null == freeWords ? "" : freeWords.replaceAll("㵝(.?)-(.?)㵝", "$1$2");
//        String r = resetRemaining(remaining.toString()).replaceAll("㵝(.?)-(.?)㵝", "$1$2").trim();
//        StringBuilder result = new StringBuilder();
//        if (!r.contains(" ") && f.length() > 1) {
//            String[] split = r.split("");
//            for (int i = 0; i < split.length; i++) {
//                if (split.length > i + 1) {
//                    if (f.contains(split[i] + split[i + 1])) {
//                        result.append(split[i]).append(split[i + 1]);
//                        i++;
//                    } else {
//                        result.append(split[i]).append(" ");
//                    }
//                }
//            }
//        }
//        return (result.length() == 0) ? r : result.toString();
    }

    // 提取规则，英文的，a、开头且后面有空格或中文；b、结尾且前面有空格或中文；c、前后均有空格或中文；
    public static boolean extractEnglish(String total, String part) {
        if (backChinese(total, part)) {
            return true;
        }
        if (frontChinese(total, part)) {
            return true;
        }
        if (total.contains(" " + part + " ")) {
            return true;
        }
        Pattern p = Pattern.compile("[\u4e00-\u9fa5]" + escape(part) + "[\u4e00-\u9fa5]");
        return p.matcher(total).find();
    }

    // 提取规则，英文的，a、开头且后面有空格或中文
    public static boolean backChinese(String total, String part) {
        if (total.startsWith(part + " ")) {
            return true;
        } else if (total.startsWith(part)) {
            Pattern p = Pattern.compile(escape(part) + "[\u4e00-\u9fa5]");
            return p.matcher(total).find();
        }
        return false;
    }

    // 提取规则，英文的，b、结尾且前面有空格或中文
    public static boolean frontChinese(String total, String part) {
        if (total.endsWith(" " + part)) {
            return true;
        } else if (total.endsWith(part)) {
            Pattern p = Pattern.compile("[\u4e00-\u9fa5]" + escape(part));
            return p.matcher(total).find();
        }
        return false;
    }


    // 获取标准的名称
    public static String getStandardName(String standard, String search) {
        standard = ("☷".equals(standard)) ? search : standard;
        standard = ("☷.".equals(standard)) ? (search + ".") : standard;
        standard = ("☷,".equals(standard)) ? (search + ",") : standard;
        standard = ("☷;".equals(standard)) ? (search + ";") : standard;
        standard = ("☷?".equals(standard)) ? (search + "?") : standard;
        standard = ("☷*".equals(standard)) ? (search + "*") : standard;
        standard = ("☷(".equals(standard)) ? ("(" + search + ").") : standard;
        standard = ("☷)".equals(standard)) ? ("(" + search + ")") : standard;
        standard = ("☷[".equals(standard)) ? ("[" + search + "].") : standard;
        standard = ("☷]".equals(standard)) ? ("[" + search + "]") : standard;
        standard = ("ㅐ".equals(standard)) ? resetRemaining(search) : standard;
        standard = ("ㅐ.".equals(standard)) ? (resetRemaining(search) + ".") : standard;
        standard = ("ㅐ,".equals(standard)) ? (resetRemaining(search) + ",") : standard;
        standard = ("ㅐ;".equals(standard)) ? (resetRemaining(search) + ";") : standard;
        standard = ("ㅐ?".equals(standard)) ? (resetRemaining(search) + "?") : standard;
        standard = ("ㅐ*".equals(standard)) ? (resetRemaining(search) + "*") : standard;
        standard = ("ㅐ(".equals(standard)) ? ("(" + search + ").") : standard;
        standard = ("ㅐ)".equals(standard)) ? ("(" + search + ")") : standard;
        standard = ("ㅐ[".equals(standard)) ? ("[" + search + "].") : standard;
        standard = ("ㅐ]".equals(standard)) ? ("[" + search + "]") : standard;
        return standard;
    }

    // 正则时，需要将普通字符串进行转义。
    public static String escape(String part) {
        return part.replace("\\", "\\\\").replace(".", "\\.").replace("^", "\\^").replace("$", "\\$").replace("(", "\\(").replace(")", "\\)")
                .replace("*", "\\*").replace("+", "\\+").replace("?", "\\?").replace("{", "\\{").replace("}", "\\}")
                .replace("[", "\\[").replace("]", "\\]").replace("|", "\\|");
    }

    // 提取规则，中文的，前面的不管，后面的，如果是中文或空格或以此结尾可以提取，如果是英文和数字，则不能提取。
    public static boolean extractChinese(String total, String part) {
        if (total.contains(part + " ")) {
            return true;
        }
        if (total.endsWith(part)) {
            return true;
        }
        Pattern p = Pattern.compile(escape(part) + "[\u4e00-\u9fa5]");
        return p.matcher(total).find();
    }

    public static void setToSetList(Set<String> cnAbKeySet, Set<List<String>> abSetList) {
        for (String s : cnAbKeySet) {
            if (s.length() > 1) {
                List<String> list = new ArrayList<>();
                list.add(s);
                abSetList.add(list);
            }
        }
    }

    public static void addSetList(Map<String, String> originalAndMeshMap, List<String> keywordList, Set<List<String>> kwSetList, boolean limit) {
        for (String key : keywordList) {
            List<String> list = new ArrayList<>();
            key = key.replaceAll(" +", " ").trim();
            key = key.endsWith(".") ? key.substring(0, key.length() - 1) : key;
            if (key.length() > 1) {
                if (limit) {
                    if (key.length() > 12 || key.contains(" ")) {
                        list.add(key);
                        if (originalAndMeshMap.containsKey(key)) {
                            list.add(originalAndMeshMap.get(key));
                            list.add("m");
                        }
                        kwSetList.add(list);
                    }
                } else {
                    list.add(key);
                    if (originalAndMeshMap.containsKey(key)) {
                        list.add(originalAndMeshMap.get(key));
                        list.add("m");
                    }
                    kwSetList.add(list);
                }
            }
        }
    }

    public static void addAllSet(Set<List<String>> tiSetList, Set<String> allSet) {
        for (List<String> list : tiSetList) {
            for (String s : list) {
                if (!"m".equals(s) && s.length() > 2) {
                    allSet.add(s);
                }
            }
        }
    }


    public static En.AbKeyMethod getCnAbKeyMethod(String cnAb) {
        En.AbKeyMethod abKeyMethod = new En.AbKeyMethod();
        if (null != cnAb && cnAb.length() > 5) {
            cnAb = "。" + cnAb.replaceAll("(\\.|\\. |。|。 |^|\n)背景和目的", "。Ł1ł").replaceAll("(\\.|\\. |。|。 |^|\n)材料与方法", "。Ł2ł")
                    .replaceAll("(\\.|\\. |。|。 |^|\n)(结果|目的|背景|结论|方法|讨论)([ :\n])", "。ĺļ$2:ҁҘ");
            cnAb = (!cnAb.contains("Ł1ł")) ? cnAb.replaceFirst("背景和目的", "ĺļ背景和目的ҁҘ") : cnAb.replace("Ł1ł", "ĺļ背景和目的ҁҘ");
            cnAb = (!cnAb.contains("Ł2ł")) ? cnAb.replaceFirst("材料与方法", "ĺļ材料与方法ҁҘ") : cnAb.replace("Ł2ł", "ĺļ材料与方法ҁҘ");
            cnAb = (!cnAb.contains("结果:ҁҘ")) ? cnAb.replaceFirst("([\\u4e00-\\u9fa5])(\\.|\\. |。|。 )(结果)", "$1。ĺļ$3:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("目的:ҁҘ")) ? cnAb.replaceFirst("([\\u4e00-\\u9fa5])(\\.|\\. |。|。 )(目的)", "$1。ĺļ$3:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("背景:ҁҘ")) ? cnAb.replaceFirst("([\\u4e00-\\u9fa5])(\\.|\\. |。|。 )(背景)", "$1。ĺļ$3:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("结论:ҁҘ")) ? cnAb.replaceFirst("([\\u4e00-\\u9fa5])(\\.|\\. |。|。 )(结论)", "$1。ĺļ$3:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("方法:ҁҘ")) ? cnAb.replaceFirst("([\\u4e00-\\u9fa5])(\\.|\\. |。|。 )(方法)", "$1。ĺļ$3:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("讨论:ҁҘ")) ? cnAb.replaceFirst("([\\u4e00-\\u9fa5])(\\.|\\. |。|。 )(讨论)", "$1。ĺļ$3:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("结果:ҁҘ")) ? cnAb.replaceFirst("(\\.|\\. |。|。 |^|\n)(结果)", "。ĺļ$2:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("目的:ҁҘ")) ? cnAb.replaceFirst("(\\.|\\. |。|。 |^|\n)(目的)", "。ĺļ$2:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("背景:ҁҘ")) ? cnAb.replaceFirst("(\\.|\\. |。|。 |^|\n)(背景)", "。ĺļ$2:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("结论:ҁҘ")) ? cnAb.replaceFirst("(\\.|\\. |。|。 |^|\n)(结论)", "。ĺļ$2:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("方法:ҁҘ")) ? cnAb.replaceFirst("(\\.|\\. |。|。 |^|\n)(方法)", "。ĺļ$2:ҁҘ") : cnAb;
            cnAb = (!cnAb.contains("讨论:ҁҘ")) ? cnAb.replaceFirst("(\\.|\\. |。|。 |^|\n)(讨论)", "。ĺļ$2:ҁҘ") : cnAb;
            cnAb = cnAb.startsWith("。") ? cnAb.substring("。".length()).trim() : cnAb.trim();
            String[] split = cnAb.split("ĺļ");
            List<En.Trans> backgroundList = new ArrayList<>();
            List<En.Trans> discussionList = new ArrayList<>();
            List<En.Trans> methodsList = new ArrayList<>();
            List<En.Trans> resultsList = new ArrayList<>();
            for (String s : split) {
                s = s.endsWith("\n。") ? s.substring(0, s.length() - "\n。".length()) : s;
                s = s.replace("\n", " ").replace("　", " ").replaceAll(" +", " ").trim();
                if (!"。".equals(s) && s.contains("ҁҘ")) {
                    cnAddTrans("背景和目的:ҁҘ", s, "background and purpose", backgroundList);
                    cnAddTrans("材料与方法:ҁҘ", s, "materials and methods", methodsList);
                    cnAddTrans("结果:ҁҘ", s, "results", resultsList);
                    cnAddTrans("目的:ҁҘ", s, "purpose", backgroundList);
                    cnAddTrans("背景:ҁҘ", s, "background", backgroundList);
                    cnAddTrans("结论:ҁҘ", s, "conclusion", resultsList);
                    cnAddTrans("方法:ҁҘ", s, "methods", methodsList);
                    cnAddTrans("讨论:ҁҘ", s, "discussion", discussionList);
                }
            }
            // 一般来说，不允许只出现一项或二项，而至少应该有三项才是完整的，所以当不足3项时，直接返回空的，只有满足时才返回。
            if ((backgroundList.size() + discussionList.size() + methodsList.size() + resultsList.size()) > 2) {
                abKeyMethod.setBackground(JSONUtil.toJsonStr(backgroundList));
                abKeyMethod.setDiscussion(JSONUtil.toJsonStr(discussionList));
                abKeyMethod.setMethods(JSONUtil.toJsonStr(methodsList));
                abKeyMethod.setResults(JSONUtil.toJsonStr(resultsList));
            }
        }
        return abKeyMethod;
    }

    public static void cnAddTrans(String cnName, String info, String label, List<En.Trans> list) {
        if (info.contains(cnName)) {
            En.Trans trans = new En.Trans();
            trans.setCn(info.replace("ҁҘ", "").replace(":：", ": "));
            trans.setEn("");
            trans.setLabel(label);
            list.add(trans);
        }
    }

    public static void getCnAbbrKeyWord(String cnAb, String fullAbbrKeyWordTable, Integer aid, Set<List<String>> abbSet, Integer pmId, String ut, Map<List<String>, Integer> keyWordSumMap) {
        if (null != cnAb && cnAb.length() > 10) {
            cnAb = cnAb.replace("，", ", ").replaceAll(" +", " ").replace("（", "(").replace("）", ")").replace("( ", "(").replace(" )", ")").trim();
            if (cnAb.contains("(") && cnAb.contains(")")) {
                Matcher matcher = Pattern.compile("\\(([^()]+)\\)").matcher(cnAb);
                if (matcher.find()) {
                    while (matcher.find()) {
                        String s = matcher.group(1);
                        String[] split = s.split(",");
                        if (split.length == 2 && s.matches(".*[a-z].*[A-Z].*") && s.contains(",") && s.contains(" ") && !s.contains("=") && !s.matches(".*[\u4e00-\u9fa5].*")) {
                            String full = split[0];
                            String abbr = split[1];
                            String equals = fullAndAbbrEquals(full, abbr);
                            if (!"false".equals(equals)) {
                                full = ("true".equals(equals)) ? full : equals;
                                full = (full.startsWith("(") || full.startsWith(")")) ? full.substring(1) : full;
                                full = full.startsWith("\"") && full.endsWith("\"") ? full.substring(1, full.length() - 1) : full;
                                full = full.startsWith("'") && full.endsWith("'") ? full.substring(1, full.length() - 1) : full;
//                                System.out.println("匹配成功 full:" + full + " abbr:" + abbr);
                                insert(fullAbbrKeyWordTable, s, full, abbr, 1, aid, pmId, ut);
                                if (isKeyWordFull(full) && isKeyWordAbbr(abbr)) {
                                    full = full.endsWith(".") ? full.substring(0, full.length() - 1) : full;
                                    abbr = abbr.endsWith(".") ? abbr.substring(0, abbr.length() - 1) : abbr;
                                    List<String> fullAbbrList = new ArrayList<>();
                                    fullAbbrList.add(full.toLowerCase());
                                    fullAbbrList.add(abbr);
                                    abbSet.add(fullAbbrList);
                                    List<String> fullList = new ArrayList<>();
                                    fullList.add(full.toLowerCase());
                                    keyWordSumMap.compute(fullList, (key, value) -> value != null ? (value + 1) : 1);
                                    keyWordSumMap.compute(fullAbbrList, (key, value) -> value != null ? (value + 1) : 1);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public static void addAbbSet(String paragraph, Set<List<String>> abbSet) {
        if (null != paragraph && paragraph.contains("(") && paragraph.contains(")")) {
            String[] paragraphSplit = paragraph.replace(".</p>", ". </p>").replace("<p>", " <p> ").replace("</strong>", " </strong> ").replace("<strong class", " <strong class")
                    .replace("/", " / ").replace("[", " [ ").replace("]", " ] ").split("\\. ");
            for (String str : paragraphSplit) {
                String[] sentenceSplit = str.split("\\)");
                for (String sentence : sentenceSplit) {
                    sentence += ")";
                    if (sentence.contains("(") && sentence.contains(")")) {
                        sentence = sentence.replace("(", " (").replace(")", ") ").replaceAll(" +", " ").replace(" -", "-")
                                .replace("- ", "-").replace("( ", "(").replace(" )", ")").trim();
                        String[] list = sentence.split(" ");
                        int end = 0;
                        for (int i = 0; i < list.length; i++) {
                            String s = list[i];
                            if (s.startsWith("(") && s.endsWith(")") && s.length() > 3) {
                                String abbr = s.substring(1, s.length() - 1);
                                if (isEnglish(abbr)) {
                                    int start = 0;
                                    for (int j = 0; j < i; j++) {
                                        if (end > start && sentence.length() > end) {
                                            String full = sentence.substring(start, end).trim();
                                            start += list[j].length() + 1;
                                            String equals = fullAndAbbrEquals(full, abbr);
                                            if (!"false".equals(equals)) {
                                                full = ("true".equals(equals)) ? full : equals;
                                                full = (full.startsWith("(") || full.startsWith(")")) ? full.substring(1) : full;
                                                full = full.startsWith("\"") && full.endsWith("\"") ? full.substring(1, full.length() - 1) : full;
                                                full = full.startsWith("'") && full.endsWith("'") ? full.substring(1, full.length() - 1) : full;
                                                if (isKeyWordFull(full) && isKeyWordAbbr(abbr)) {
                                                    full = full.endsWith(".") ? full.substring(0, full.length() - 1) : full;
                                                    abbr = abbr.endsWith(".") ? abbr.substring(0, abbr.length() - 1) : abbr;
                                                    List<String> fullAbbrList = new ArrayList<>();
                                                    fullAbbrList.add(full.toLowerCase());
                                                    fullAbbrList.add(abbr);
                                                    abbSet.add(fullAbbrList);
                                                }
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                            end += list[i].length() + 1;
                        }
                    }
                }
            }
        }
    }

    // 提取关键词，此类词的特征是前面是全写，后面有一个括号，比如：hepatocyte growth factor (HGF)->hepatocyte growth factor
    public static void getAbbrKeyWord(String paragraph, String fullAbbrKeyWordTable, Integer aid, Set<List<String>> abbSet, Integer pmId, String ut, Map<List<String>, Integer> keyWordSumMap) {
        if (null != paragraph && paragraph.contains("(") && paragraph.contains(")") && null != aid && aid > 0) {
            String[] paragraphSplit = paragraph.replace(".</p>", ". </p>").replace("<p>", " <p> ").replace("</strong>", " </strong> ").replace("<strong class", " <strong class")
                    .replace("/", " / ").replace("[", " [ ").replace("]", " ] ").split("\\. ");
            for (String str : paragraphSplit) {
                String[] sentenceSplit = str.split("\\)");
                for (String sentence : sentenceSplit) {
                    sentence += ")";
                    String original = sentence;
//                    System.out.println("original:" + original);
                    if (sentence.contains("(") && sentence.contains(")")) {
                        sentence = sentence.replace("(", " (").replace(")", ") ").replaceAll(" +", " ").replace(" -", "-")
                                .replace("- ", "-").replace("( ", "(").replace(" )", ")").trim();
                        String[] list = sentence.split(" ");
                        int end = 0;
                        for (int i = 0; i < list.length; i++) {
                            String s = list[i];
                            if (s.startsWith("(") && s.endsWith(")") && s.length() > 3) {
                                String abbr = s.substring(1, s.length() - 1);
                                if (isEnglish(abbr)) {
                                    int start = 0;
                                    boolean bool = true;
                                    for (int j = 0; j < i; j++) {
                                        if (end > start && sentence.length() > end) {
                                            String full = sentence.substring(start, end).trim();
                                            start += list[j].length() + 1;
                                            String equals = fullAndAbbrEquals(full, abbr);
                                            if (!"false".equals(equals)) {
                                                full = ("true".equals(equals)) ? full : equals;
                                                bool = false;
                                                full = (full.startsWith("(") || full.startsWith(")")) ? full.substring(1) : full;
                                                full = full.startsWith("\"") && full.endsWith("\"") ? full.substring(1, full.length() - 1) : full;
                                                full = full.startsWith("'") && full.endsWith("'") ? full.substring(1, full.length() - 1) : full;
//                                                System.out.println("匹配成功 full:" + full + " abbr:" + abbr);
                                                insert(fullAbbrKeyWordTable, original, full, abbr, 1, aid, pmId, ut);
                                                if (isKeyWordFull(full) && isKeyWordAbbr(abbr)) {
                                                    full = full.endsWith(".") ? full.substring(0, full.length() - 1) : full;
                                                    abbr = abbr.endsWith(".") ? abbr.substring(0, abbr.length() - 1) : abbr;
                                                    List<String> fullAbbrList = new ArrayList<>();
                                                    fullAbbrList.add(full.toLowerCase());
                                                    fullAbbrList.add(abbr);
                                                    abbSet.add(fullAbbrList);
                                                    List<String> fullList = new ArrayList<>();
                                                    fullList.add(full.toLowerCase());
                                                    keyWordSumMap.compute(fullList, (key, value) -> value != null ? (value + 1) : 1);
                                                    keyWordSumMap.compute(fullAbbrList, (key, value) -> value != null ? (value + 1) : 1);
                                                }
                                                break;
                                            }
                                        }
                                    }
                                    if (bool) {
                                        insert(fullAbbrKeyWordTable, original, "", abbr, 0, aid, pmId, ut);
                                    }
                                }
                            }
                            end += list[i].length() + 1;
                        }
                    }
                }
            }
        }
    }


    // 判断词组与缩写的单词之间是否相等，如果词组内各单词的首字母与缩写的单词的各个首字母相等，或者首字母相等后半部分包含，可以认定为相等。比如：Mendelian randomization-Egger (MR-Egger)或hepatocyte growth factor (HGF)，要求全写的词组full必须包含空格，缩写的单词abbr必须大于1位，且首字母必须相同。
    public static String fullAndAbbrEquals(String full, String abbr) {
        if (null != full && null != abbr && (full.trim().contains(" ") || full.contains("-") || full.length() > 11) && abbr.length() > 1 && full.length() >= abbr.length()) {
            abbr = NumberUtil.isInteger(abbr.substring(0, 1)) ? abbr.substring(1) : abbr;
            // of 3-methoxy-O-desmethyl encainide (MODE)
            String[] fSplit = full.toLowerCase().split(" ");
            String[] aSplit = abbr.toLowerCase().split("");
            if (fSplit[0].contains("-" + aSplit[0]) && fSplit.length == 2 && aSplit.length == 4) {
                if (fSplit[0].contains("-" + aSplit[1]) && fSplit[0].contains("-" + aSplit[2]) && fSplit[1].startsWith(aSplit[3])) {
                    return full;
                }
            }
            // labelled with an anti-human leukocyte antigen (HLA)
            if (full.toLowerCase().contains("-" + abbr.substring(0, 1).toLowerCase())) {
                String[] fullSplit = full.toLowerCase().split(" ");
                String[] abbrSplit = abbr.toLowerCase().split("");
                int isOk = 0;
                String result = "";
                for (int i = 0; i < fullSplit.length; i++) {
                    int l = i + fullSplit.length - abbrSplit.length;
                    if (l > 0 && fullSplit.length > l && abbrSplit.length > i) {
                        String f = fullSplit[i + fullSplit.length - abbrSplit.length];
                        String a = abbrSplit[i];
                        if (f.contains("-" + a) || f.startsWith(a)) {
                            isOk++;
                            result += f + " ";
                        } else {
                            result = "";
                            isOk = 0;
                            break;
                        }
                    }
                }
                if (isOk >= abbrSplit.length) {
                    return result.trim();
                }
            }

            // Three-domain Interoceptive Sensations Questionnaire (THISQ)
            if (fSplit.length == 4 && aSplit.length == 5 && fSplit[0].contains("-")) {
                if (fSplit[0].startsWith(aSplit[0]) && fSplit[0].contains(aSplit[1]) && fSplit[1].startsWith(aSplit[2]) && fSplit[2].startsWith(aSplit[3]) && fSplit[3].startsWith(aSplit[4])) {
//                    System.out.println("3.2");
                    return "true";
                }
            }

            if (fSplit.length == aSplit.length) {
                boolean bool = true;
                for (int i = 0; i < fSplit.length; i++) {
                    if (!fSplit[i].substring(0, 1).equals(aSplit[i])) {
                        bool = false;
                        break;
                    }
                }
                if (bool) {
//                    System.out.println("2.1");
                    return "true";
                }
            }

            //In this study, reduced graphene oxide-TiO₂ (RGO-TiO₂)
            String[] fs = full.toLowerCase().split("-");
            String[] as = abbr.toLowerCase().split("-");
//            System.out.println("....~~~full:" + full + " abbr:" + abbr + " fs.length:" + fs.length + " as.length:" + as.length);
            if (fs.length == as.length && as.length == 2 && fs[1].equals(as[1]) && fs[1].length() > 1) {
                String[] f = fs[0].split(" ");
                String[] a = as[0].split("");
                if (f.length == a.length && f.length > 1) {
                    boolean bool = true;
                    for (int i = 0; i < f.length; i++) {
                        if (!f[i].substring(0, 1).equals(a[i])) {
                            bool = false;
                            break;
                        }
                    }
                    if (bool) {
//                        System.out.println("2.1");
                        return "true";
                    }
                }
            }

            if (fs.length == as.length && as.length == 3) {
                if (fs[0].startsWith(as[0]) && fs[1].startsWith(as[1])) {
//                    System.out.println("fs[2]:" + fs[2] + " as[2]:" + as[2]);
                    String[] f = fs[2].trim().split(" ");
                    String[] a = as[2].trim().split("");
                    if (f.length == a.length && f.length > 1) {
                        boolean bool = true;
                        for (int i = 0; i < f.length; i++) {
                            if (!f[i].substring(0, 1).equals(a[i])) {
                                bool = false;
                                break;
                            }
                        }
                        if (bool) {
//                            System.out.println("2.2");
                            return "true";
                        }
                    } else if (fs[2].trim().equals(as[2].trim())) {
//                        System.out.println("2.2");
                        return "true";
                    }
                }
            }

            full = full.startsWith("-") ? full.substring(1) : full;

            if (full.replace("(", "").replace("\"", "").replace("'", "").replace(")", "").substring(0, 1).equalsIgnoreCase(abbr.substring(0, 1))) {
                abbr = abbr.contains("α") && !full.contains("α") ? abbr.replace("α", " alpha ") : abbr;
                // Transforming growth factor-beta 1 (TGF-β1)
                abbr = abbr.contains("β") && !full.contains("β") ? abbr.replace("β", " beta ") : abbr;
                abbr = abbr.contains("γ") && !full.contains("γ") ? abbr.replace("γ", " gamma ") : abbr;
                abbr = abbr.contains("δ") && !full.contains("δ") ? abbr.replace("δ", " delta ") : abbr;
                abbr = abbr.contains("ε") && !full.contains("ε") ? abbr.replace("ε", " epsilon ") : abbr;
                abbr = abbr.contains("ζ") && !full.contains("ζ") ? abbr.replace("ζ", " zeta ") : abbr;
                abbr = abbr.contains("η") && !full.contains("η") ? abbr.replace("η", " eta ") : abbr;
                abbr = abbr.contains("θ") && !full.contains("θ") ? abbr.replace("θ", " theta ") : abbr;
                abbr = abbr.contains("ι") && !full.contains("ι") ? abbr.replace("ι", " iota ") : abbr;
                abbr = abbr.contains("κ") && !full.contains("κ") ? abbr.replace("κ", " kappa ") : abbr;
                abbr = abbr.contains("λ") && !full.contains("λ") ? abbr.replace("λ", " lambda ") : abbr;
                abbr = abbr.contains("μ") && !full.contains("μ") ? abbr.replace("μ", " mu ") : abbr;
                abbr = abbr.contains("ν") && !full.contains("ν") ? abbr.replace("ν", " nu ") : abbr;
                abbr = abbr.contains("ξ") && !full.contains("ξ") ? abbr.replace("ξ", " xi ") : abbr;
                abbr = abbr.contains("ο") && !full.contains("ο") ? abbr.replace("ο", " omicron ") : abbr;
                abbr = abbr.contains("π") && !full.contains("π") ? abbr.replace("π", " pi ") : abbr;
                abbr = abbr.contains("ρ") && !full.contains("ρ") ? abbr.replace("ρ", " rho ") : abbr;
                abbr = abbr.contains("σ") && !full.contains("σ") ? abbr.replace("σ", " sigma ") : abbr;
                abbr = abbr.contains("τ") && !full.contains("τ") ? abbr.replace("τ", " tau ") : abbr;
                abbr = abbr.contains("υ") && !full.contains("υ") ? abbr.replace("υ", " upsilon ") : abbr;
                abbr = abbr.contains("φ") && !full.contains("φ") ? abbr.replace("φ", " phi ") : abbr;
                abbr = abbr.contains("χ") && !full.contains("χ") ? abbr.replace("χ", " chi ") : abbr;
                abbr = abbr.contains("ψ") && !full.contains("ψ") ? abbr.replace("ψ", " psi ") : abbr;
                abbr = abbr.contains("ω") && !full.contains("ω") ? abbr.replace("ω", " omega ") : abbr;
                if (full.equalsIgnoreCase(abbr)) {
//                    System.out.println("1");
                    return "true";
                }

                // 'Miniature Inverted-repeat Transposable Element' -> MITE
                if (full.startsWith("'") && full.endsWith("'")) {
                    full = full.substring(1, full.length() - 1);
                    String[] fullSplit = full.split(" ");
                    String[] abbrSplit = abbr.split("");
                    if (fullSplit.length == abbrSplit.length) {
                        boolean bool = true;
                        for (int i = 0; i < fullSplit.length; i++) {
                            if (!fullSplit[i].substring(0, 1).equals(abbrSplit[i])) {
                                bool = false;
                                break;
                            }
                        }
                        if (bool) {
//                            System.out.println("2");
                            return "true";
                        }
                    }
                }

                // "Miniature Inverted-repeat Transposable Element" -> MITE
                if (full.startsWith("\"") && full.endsWith("\"")) {
                    full = full.substring(1, full.length() - 1);
                    String[] fullSplit = full.split(" ");
                    String[] abbrSplit = abbr.split("");
                    if (fullSplit.length == abbrSplit.length) {
                        boolean bool = true;
                        for (int i = 0; i < fullSplit.length; i++) {
                            if (!fullSplit[i].substring(0, 1).equals(abbrSplit[i])) {
                                bool = false;
                                break;
                            }
                        }
                        if (bool) {
//                            System.out.println("2");
                            return "true";
                        }
                    }
                }

                // 如果缩写不包含"-"，则可以把全写词组中的给予替换成空格，若包含，则不可以，比如：autosomal-dominant PKD(ADPKD)
                full = full.replace("-", " ").replace("(", "").replace(")", "").toLowerCase().replaceAll(" +", " ").trim();
                full = full.startsWith("\"") && full.endsWith("\"") ? full.substring(1, full.length() - 1) : full;
                abbr = abbr.replace("-", "").toLowerCase().replaceAll(" +", " ").trim();
                String[] fullSplit = full.split(" ");
                String[] abbrSplit = abbr.split("");
//                System.out.println("~~~full:" + full + " abbr:" + abbr + " fullSplit.length:" + fullSplit.length + " abbrSplit.length:" + abbrSplit.length);

                // leucine phenylalanine   leuphe
                if (fullSplit.length == 2 && !abbr.contains(" ")) {
                    for (int i = abbr.length(); i > 0; i--) {
                        if (fullSplit[0].startsWith(abbr.substring(0, i))) {
                            if (fullSplit[1].startsWith(abbr.substring(i))) {
                                return "true";
                            }
                        }
                    }
                }

                // 如果词组的个数与缩写单词的个数相同，且词组内各单词的首字母与缩写的单词的各个首字母相等，使用下面的方法。
                if (fullSplit.length == abbrSplit.length) {
                    boolean bool = true;
                    for (int i = 0; i < fullSplit.length; i++) {
                        if (!fullSplit[i].substring(0, 1).equals(abbrSplit[i])) {
                            bool = false;
                            break;
                        }
                    }
                    if (bool) {
//                        System.out.println("2");
                        return "true";
                    }
                }
                // full:nanosecond pulsed electric field abbr:nsPEF
                if (fullSplit.length == 4 && abbrSplit.length == 5) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].startsWith(abbrSplit[2]) && fullSplit[2].startsWith(abbrSplit[3]) && fullSplit[3].startsWith(abbrSplit[4])) {
//                        System.out.println("3");
                        return "true";
                    }
                    // full:|vascular smooth muscle cells| abbr:|VSMCs|
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[2].startsWith(abbrSplit[2]) && fullSplit[3].startsWith(abbrSplit[3]) && fullSplit[3].endsWith(abbrSplit[4])) {
//                        System.out.println("3.");
                        return "true";
                    }
                    //INTRODUCTION: Young pregnant and postpartum women living with HIV (WLHIV)
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[3].equals(abbrSplit[2] + abbrSplit[3] + abbrSplit[4])) {
//                        System.out.println("3.4");
                        return "true";
                    }
                }
                // Long non-coding RNA (lncRNA)
                if (fullSplit.length == 4 && abbrSplit.length == 6) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[2].startsWith(abbrSplit[2]) && fullSplit[3].equals(abbrSplit[3] + abbrSplit[4] + abbrSplit[5])) {
//                        System.out.println("3.3");
                        return "true";
                    }
                }

                // The effects of a diet enriched with 25% raw soya bean flour (RSF)
                if (fullSplit.length == 4 && abbrSplit.length == 3) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[3].startsWith(abbrSplit[2])) {
//                        System.out.println("3.4");
                        return "true";
                    }
                }

                // full:pancreatic neuroendocrine tumors abbr:PanNETs
                if (fullSplit.length == 3 && abbr.length() > 3) {
                    boolean bool = true;
                    int sum = 0;
                    T:
                    for (int i = abbr.length(); i > 0; i--) {
                        if (fullSplit[0].startsWith(abbr.substring(0, i))) {
                            // 第一次，abbr是完整的，第二次时，abbr变成了nets
                            String second = abbr.substring(i);
                            for (int j = second.length(); j > 0; j--) {
                                if (fullSplit[1].startsWith(second.substring(0, j))) {
                                    // 第三次时，abbr变成了ts
                                    String third = second.substring(j);
                                    String[] split = third.split("");
                                    // 如果全部包含，则维持bool仍为true，否则有任一个不是则为false。为什么要加一个变量sum？因为bool的默认值是true，如果程序走不到这一步呢？这样bool仍为true，加上sum后，要求其必须执行++，就可以确保程序走到了这一步。
                                    for (String s : split) {
                                        if (fullSplit[2].contains(s)) {
                                            sum++;
                                        } else {
                                            bool = false;
                                            break T;
                                        }
                                    }
                                    break T;
                                }
                            }
                        }
                    }
                    if (bool && sum > 0) {
//                        System.out.println("4");
                        return "true";
                    }
                }

                // We report herein a rare case of malignant gastrointestinal stromal tumor (GIST)
                if (fullSplit.length == 3 && abbrSplit.length == 4) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].startsWith(abbrSplit[2]) && fullSplit[2].startsWith(abbrSplit[3])) {
//                        System.out.println("4.1");
                        return "true";
                    }

                    // involvement of a ventral prefrontal cortex (VPFC)
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[2]) && fullSplit[2].startsWith(abbrSplit[3])) {
//                        System.out.println("4.2");
                        return "true";
                    }

                }
                // chemotherapy (CT)
                if (fullSplit.length == 1 && abbrSplit.length == 2) {
//                    System.out.println("5");
                    return fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) ? "true" : "false";
                }
                // full:lipopolysaccharide abbr:LPS
                if (fullSplit.length == 1 && abbrSplit.length == 3) {
//                    System.out.println("6");
                    return fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) && fullSplit[0].contains(abbrSplit[2]) ? "true" : "false";
                }
                // interquartile range (IQR)
                if (fullSplit.length == 2 && abbrSplit.length == 3) {
                    if (fullSplit[0].startsWith(abbrSplit[0])) {
                        if (fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].startsWith(abbrSplit[2])) {
//                            System.out.println("7");
                            return "true";
                        }
                        if (fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[2])) {
//                            System.out.println("8");
                            return "true";
                        }
                    }
                }

//                System.out.println("~~~full:" + full + " abbr:" + abbr + " fullSplit.length:" + fullSplit.length + " abbrSplit.length:" + abbrSplit.length);
                // the tissue non specific alkaline phosphatase (TNSALP)
                if (fullSplit.length == 5 && abbrSplit.length == 6) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[2].startsWith(abbrSplit[2]) && fullSplit[3].startsWith(abbrSplit[3]) && fullSplit[3].contains(abbrSplit[4]) && fullSplit[4].startsWith(abbrSplit[5])) {
//                        System.out.println("3.3");
                        return "true";
                    }
                    // ameliorates oxidative stress and dysfunctions of human retinal microvascular endothelial cells (hRMECs)
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[2].startsWith(abbrSplit[2]) && fullSplit[3].startsWith(abbrSplit[3]) && fullSplit[4].startsWith(abbrSplit[4]) && fullSplit[4].endsWith(abbrSplit[5])) {
//                        System.out.println("3.4");
                        return "true";
                    }
                }

                // ameliorates oxidative stress and dysfunctions of human retinal microvascular endothelial cells (hRMECs)

                // full:pseudorandom sinusoidal abbr:prss
                if (fullSplit.length == 2 && abbrSplit.length == 4) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[2])) {
                        if (fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[3])) {
//                            System.out.println("9");
                            return "true";
                        }
                    }
                    // Considerable evidence has implicated matrix metalloproteinases (MMPs)
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[2]) && fullSplit[1].endsWith(abbrSplit[3])) {
//                        System.out.println("9.1");
                        return "true";
                    }
                }
                return isEquals(full, abbr);
            }
        }
        return "false";
    }


    // 功能：查看全写的词组（full）与缩写的词组（abbr）之间，是否匹配。采用的方法是先循环full，从前往后推，再循环abbr，如果各单词的首字母是，则isOk++，若不是则退出，最后若isOk >= abbrSplit.length则判定是。
    public static String isEquals(String full, String abbr) {
        full = full.replace("\"", "");
        String[] fullSplit = full.split(" ");
        String[] abbrSplit = abbr.split("");
        int fLength = fullSplit.length;
        int aLength = abbrSplit.length;
        String result = "";
        if (fLength > aLength) {
            for (int i = 0; i < fullSplit.length; i++) {
                int isOk = 0;
                StringBuilder str = new StringBuilder();
                for (int j = 0; j < abbrSplit.length; j++) {
                    String a = abbrSplit[j];
                    if (fullSplit.length > i + j) {
                        if (fullSplit[i + j].startsWith(a)) {
                            isOk++;
                            str.append(fullSplit[i + j]).append(" ");
                        } else {
                            isOk = 0;
                            break;
                        }
                    }
                }
                result = isOk >= abbrSplit.length ? str.toString().trim() : result;
            }
            // 上面的方法是直接匹配，不允许中间有介词，下面的方法允许有介词。
            if (result.isEmpty()) {
                int add = 0;
                for (int i = 0; i < fullSplit.length; i++) {
                    int isOk = 0;
                    StringBuilder str = new StringBuilder();
                    for (int j = 0; j < abbrSplit.length; j++) {
                        String a = abbrSplit[j];
                        if (fullSplit.length > add + i + j) {
                            String f = fullSplit[add + i + j];
                            if (("a".equals(f) || "an".equals(f) || "of".equals(f) || "in".equals(f) || "on".equals(f) || "the".equals(f) || "and".equals(f) || "is".equals(f) || "at".equals(f) || "for".equals(f) || "with".equals(f) ||
                                    "by".equals(f) || "from".equals(f) || "to".equals(f) || "between".equals(f) || "among".equals(f) || "under".equals(f) || "above".equals(f) || "voor".equals(f))) {
                                add += 1;
                                // 为什么大于2？in national institutes of health stroke scale->NIHSS
                                if (add > 2) {
                                    str.append(f).append(" ");
                                }
                            }
                            if (fullSplit.length > add + i + j) {
                                if (fullSplit[add + i + j].startsWith(a)) {
                                    str.append(fullSplit[add + i + j]).append(" ");
                                    isOk++;
                                } else {
                                    isOk = 0;
                                    break;
                                }
                            }
                        }
                    }
                    result = isOk >= abbrSplit.length ? str.toString().trim() : result;
                }
            }
        }
//        System.out.println("_____full:" + full + " abbr:" + abbr + " result:" + result);
        return result.isEmpty() ? "false" : result;
    }

    // 正则判断是否包含大小写英文字母
    public static boolean isEnglish(String str) {
        Pattern p = Pattern.compile("[a-zA-Z]");
        Matcher m = p.matcher(str);
        return m.find();
    }

    public static void insert(String fullAbbrKeyWordTable, String original, String full, String abbr, int isOk, int aid, Integer pmId, String ut) {
        try {
            Db.use().insert(Entity.create(fullAbbrKeyWordTable).set("original", original).set("full", full).set("abbr", abbr).set("is_ok", isOk).set("aid", aid).set("is_ok", isOk).set("pmId", pmId).set("ut", ut).set("aid", aid).set("date", LocalDateTime.now()));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 1. 关键词出现多少次才可以认定为关键词呢？在测试程序中大于0即可，实际使用时，以大于50次为标准，设置在方法：insertKeyWordSumMap中（if (sum > 0 || (keyWordList.size() == 2 && sum > 0) || (keyWordList.size() == 3 && sum > 0)) {），
    // 2. keyWordList.size() == 2即是全写与缩写，数量可以设置为10次。keyWordList.size() == 3即是mesh词，数量可以设置为1次。中文的关键词，大于10次即可。
    public static Map<String, String> insertKeyWordSumMap(String keyWordSumTable, Map<List<String>, Integer> keyWordSumMap, Map<String, String> keyWordAndMeshMap, Map<String, String> md5Map,
                                                          Map<String, Integer> cnKeyWordSumMap, Map<String, String> reKwMap) throws SQLException {
        Map<String, String> keyWordAndStandardMap = new ConcurrentHashMap<>();
        Db.use().execute("truncate table " + keyWordSumTable);
        keyWordSumMap.keySet().parallelStream().forEach(keyWordList -> {
            Integer sum = keyWordSumMap.get(keyWordList);
            if (sum > 50 || (keyWordList.size() == 2 && sum > 10) || (keyWordList.size() == 3 && sum > 1)) {
                String kw = reRemaining(Utils.reKw(reKwMap, keyWordList.get(0)));
                String standard = keyWordAndMeshMap.getOrDefault(keyWordList.get(0), kw);
                En.InfoAndStandard infoAndStandard = getInfoAndStandard(kw, standard, md5Map, reKwMap);
                keyWordAndStandardMap.put(infoAndStandard.getInfo(), infoAndStandard.getStandard());
            }
            try {
                Db.use().insert(Entity.create(keyWordSumTable).set("key_word", JSONUtil.toJsonStr(keyWordList)).set("sum", keyWordSumMap.get(keyWordList)).set("is_cn", 0));
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
        keyWordSumMap.clear();

        cnKeyWordSumMap.keySet().parallelStream().forEach(cnKey -> {
            Integer sum = cnKeyWordSumMap.get(cnKey);
            try {
                Db.use().insert(Entity.create(keyWordSumTable).set("key_word", cnKey).set("sum", sum).set("is_cn", 1));
            } catch (SQLException e) {
                e.printStackTrace();
            }
            if (sum > 30) {
                En.InfoAndStandard infoAndStandard = getInfoAndStandard(cnKey, cnKey, md5Map, reKwMap);
                keyWordAndStandardMap.put(infoAndStandard.getInfo(), infoAndStandard.getStandard());
            }
        });
        cnKeyWordSumMap.clear();
        return keyWordAndStandardMap;
    }

    public static void readMesh(String readTable, String autoId, String field, boolean print, int allTotal, Map<String, String> md5Map,
                                Map<String, String> keyWordAndMeshMap, Map<String, String> originalAndMeshMap, Map<String, String> reKwMap) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        int onePageNum = 1000;
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + readTable);
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + readTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            AtomicInteger sumNum = new AtomicInteger(0);
            int showNum = Math.max((tableList.size() / allTotal), 1) + (tableList.size() % allTotal);
            AtomicInteger startNum = new AtomicInteger(1);
            tableList.parallelStream().forEach(startId -> {
                try {
                    List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + readTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1));
                    if (print && sumNum.getAndIncrement() % showNum == 0) {
//                        System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startNum.getAndIncrement() + " 总次数：" + allTotal + " 表名：" + readTable + Utils.printMemoryStr() + "\033[0m");
                    }
                    for (Entity entry : tableData) {
                        String meshName = Utils.getStrField(entry, "meshName").replace("[Publication Type]", "").replace("[Subheading]", "").replace("[文献类型]", "").trim();
                        meshName = meshName.endsWith(".") ? meshName.substring(0, meshName.length() - 1).trim() : meshName;
                        List<String> kwList = strToList(Utils.getStrField(entry, "entryTerms"));
                        kwList.add(meshName);
                        for (String original : kwList) {
                            original = original.endsWith(".") ? original.substring(0, original.length() - 1).trim() : original.trim();
                            String kw2 = reRemaining(Utils.reKw(reKwMap, original));
//                            String kw2 = reRemaining(CharUtil.reKw(original));
                            En.InfoAndStandard infoAndStandard = getInfoAndStandard(kw2, meshName, md5Map, reKwMap);
                            String key = infoAndStandard.getInfo();
                            if (key.length() > 4 && key.contains(" ")) {
                                keyWordAndMeshMap.put(key, meshName);
                                if (!original.equalsIgnoreCase(meshName)) {
                                    originalAndMeshMap.put(original.toLowerCase(), meshName);
                                }
                            }
                        }
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
    }


    // 因英文下是以空格切割的，如果某个单词后面是一个句号，因切割后单词与句号是连在一起的，就无法匹配出来了，为了解决这个问题，需要双方（加MAP及匹配时）均对句号等给转换一下，存MAP时，如果info与standard值相等，则只插入info，已节约空间。
    public static En.InfoAndStandard getInfoAndStandard(String info, String standard, Map<String, String> md5Map, Map<String, String> reKwMap) {
        En.InfoAndStandard infoAndStandard = new En.InfoAndStandard();
        standard = (standard.isEmpty()) ? info : standard;
        standard = Utils.reKw(reKwMap, standard.replace("[Publication Type]", "").replace("[Subheading]", "").replace("[文献类型]", ""));
        info = reRemaining(info.replace("[Publication Type]", "").replace("[Subheading]", "").replace("[文献类型]", "")).replaceAll("( non )", " non-").replaceAll("(^non )", "non-")
                .replaceAll("( not )", " not-").replaceAll("(^not )", "not-").replace("非", "非-").replace("不", "不-").replaceAll("(.?-.?)", "㵝$1㵝");
        standard = standard.toLowerCase().replaceAll(" +", " ");
        standard = (info.equals(standard)) ? "☷" : standard;
        standard = ((info + ".").equals(standard)) ? "☷." : standard;
        standard = ((info + ",").equals(standard)) ? "☷," : standard;
        standard = ((info + ";").equals(standard)) ? "☷;" : standard;
        standard = ((info + "?").equals(standard)) ? "☷?" : standard;
        standard = ((info + "*").equals(standard)) ? "☷*" : standard;
        standard = (("(" + info + ").").equals(standard)) ? "☷(" : standard;
        standard = (("(" + info + ")").equals(standard)) ? "☷)" : standard;
        standard = (("[" + info + "].").equals(standard)) ? "☷[" : standard;
        standard = (("[" + info + "]").equals(standard)) ? "☷]" : standard;
        standard = (resetRemaining(info).equals(standard)) ? "ㅐ" : standard;
        standard = ((resetRemaining(info) + ".").equals(standard)) ? "ㅐ." : standard;
        standard = ((resetRemaining(info) + ",").equals(standard)) ? "ㅐ," : standard;
        standard = ((resetRemaining(info) + ";").equals(standard)) ? "ㅐ;" : standard;
        standard = ((resetRemaining(info) + "?").equals(standard)) ? "ㅐ?" : standard;
        standard = ((resetRemaining(info) + "*").equals(standard)) ? "ㅐ*" : standard;
        standard = (("(" + resetRemaining(info) + ").").equals(standard)) ? "ㅐ(" : standard;
        standard = (("(" + resetRemaining(info) + ")").equals(standard)) ? "ㅐ)" : standard;
        standard = (("[" + resetRemaining(info) + "].").equals(standard)) ? "ㅐ[" : standard;
        standard = (("[" + resetRemaining(info) + "]").equals(standard)) ? "ㅐ]" : standard;
        if (info.length() > 16) {
            String md5 = SecureUtil.md5(info).substring(8, 24).toLowerCase();
            md5Map.put(md5, info);
            info = md5;
        }
        int len = (Utils.isContainChinese(info)) ? 1 : 2;
        if (info.length() > len) {
            infoAndStandard.setInfo(info);
            infoAndStandard.setStandard(standard);
        }
        return infoAndStandard;
    }


    // 返回余下的，因为在匹配之前对文本作了操作，返回之前需要再次还原回来。
    public static String resetRemaining(String remaining) {
        remaining = remaining.replaceAll(" +", " ").replace(" 纃1 ", "|").replace(" 纃a ", ". ").replace(" 纃b ", " .").replace(" 纃c ", ".")
                .replace(" 纃d ", ", ").replace(" 纃e ", " ,").replace(" 纃f ", ",").replace(" 纃g ", "; ").replace(" 纃h ", " ;").replace(" 纃i ", ";")
                .replace(" 纃j ", "( ").replace(" 纃k ", " (").replace(" 纃l ", "(").replace(" 纃m ", ") ").replace(" 纃n ", " )").replace(" 纃o ", ")")
                .replace(" 纃p ", "/ ").replace(" 纃q ", " /").replace(" 纃r ", "/").replace(" 纃s ", "\" ").replace(" 纃t ", " \"").replace(" 纃u ", "\"")
                .replace(" 纃v ", ": ").replace(" 纃w ", " :").replace(" 纃x ", ":").replace(" 纃y ", "?").replace(" 纃z ", "*");
        remaining = remaining.replaceAll(" +", " ").replace("纃a", " ").replace("纃b", " ").replace("纃c", " ").replace("纃d", " ").replace("纃e", " ")
                .replace("纃f", " ").replace("纃g", " ").replace("纃h", " ").replace("纃i", " ").replace("纃j", " ").replace("纃k", " ")
                .replace("纃l", " ").replace("纃m", " ").replace("纃n", " ").replace("纃o", " ").replace("纃p", " ").replace("纃q", " ")
                .replace("纃r", " ").replace("纃s", " ").replace("纃t", " ").replace("纃u", " ").replace("纃v", " ").replace("纃w", " ")
                .replace("纃x", " ").replace("纃y", " ").replace("纃z", " ").replace("纃1", " ").replaceAll(" +", " ");
        return remaining;
    }

    public static String reRemaining(String info) {
        // Particle formation by supercritical fluid extraction and expansion process. 通过超临界流体萃取和膨胀过程形成颗粒
        info = reEndStr(info);
        info = info.toLowerCase().replaceAll(" +", " ").replace("|", " 纃1 ").replace(". ", " 纃a ").replace(" .", " 纃b ").replace(")", " 纃o ").replace(".", " 纃c ")
                .replace(", ", " 纃d ").replace(" ,", " 纃e ").replace(",", " 纃f ").replace("; ", " 纃g ").replace(" ;", " 纃h ").replace(";", " 纃i ")
                .replace("( ", " 纃j ").replace(" (", " 纃k ").replace("(", " 纃l ").replace(") ", " 纃m ").replace(" )", " 纃n ")
                .replace("/ ", " 纃p ").replace(" /", " 纃q ").replace("/", " 纃r ").replace("\" ", " 纃s ").replace(" \"", " 纃t ").replace("\"", " 纃u ")
                .replace(": ", " 纃v ").replace(" :", " 纃w ").replace(":", " 纃x ").replace("?", " 纃y ").replace("*", " 纃z ").replaceAll(" +", " ");
        return info.replace("⟪", "(").replace("⟫", ")");
    }

    public static String reEndStr(String info) {
        info = info.replaceAll(" +", " ").replace(" -", "-").replace("- ", "-");
        info = (info.endsWith(".")) ? info.substring(0, info.length() - ".".length()) : info;
        info = (info.endsWith(",")) ? info.substring(0, info.length() - ".".length()) : info;
        info = (info.endsWith(";")) ? info.substring(0, info.length() - ".".length()) : info;
        info = (info.endsWith("?")) ? info.substring(0, info.length() - "?".length()) : info;
        info = (info.endsWith("*")) ? info.substring(0, info.length() - "*".length()) : info;
        info = (info.startsWith("(") && info.endsWith(").")) ? info.substring("(".length(), info.length() - ").".length()) : info;
        info = (info.startsWith("(") && info.endsWith(")")) ? info.substring("(".length(), info.length() - ")".length()) : info;
        info = (info.startsWith("[") && info.endsWith("].")) ? info.substring("[".length(), info.length() - "].".length()) : info;
        info = (info.startsWith("[") && info.endsWith("]")) ? info.substring("[".length(), info.length() - "]".length()) : info;
        return info.replaceAll(" +", " ");
    }

    // 字符串转换为List后输出，参数len是对字符串长度的约束，一般来说List存表后的字符串应大于4位，比如：["图书馆"]
    public static List<String> strToList(String str) {
        if (null != str && str.length() > 4 && str.endsWith("\"]")) {
            try {
                List<String> list = JSONObject.parseArray(str, String.class);
                if (null != list && !list.isEmpty()) {
                    Set<String> set = new HashSet<>();
                    for (String s : list) {
                        if (!s.isEmpty()) {
                            set.add(s);
                        }
                    }
                    return new ArrayList<>(set);
                }
            } catch (Exception e) {
                System.out.println("\033[31;1m" + "--strToList err-- " + "\033[0m" + str);
            }
        }
        return new ArrayList<>();
    }

    public static En.AbKeyMethod getTransStr(Set<String> backgroundSet, Set<String> discussionSet, Set<String> methodsSet, Set<String> resultsSet, List<En.Trans> transList) {
        List<En.Trans> backgroundList = new ArrayList<>();
        List<En.Trans> discussionList = new ArrayList<>();
        List<En.Trans> methodsList = new ArrayList<>();
        List<En.Trans> resultsList = new ArrayList<>();
        for (En.Trans trans : transList) {
            String l = trans.getLabel().toLowerCase().trim();
            // 为什么作下面的两行？原因是部分pm的存在序号，比如：1) PURPOSE: To demonstrate，而这个序号在往标签中添加时，并没有去掉，但在标签集合中，是没有这着序号的，因此需要先去掉再使用。
            l = (l.startsWith("1)") || l.startsWith("2)") || l.startsWith("3)") || l.startsWith("4)") || l.startsWith("5)") || l.startsWith("6)") || l.startsWith("7)") || l.startsWith("8)") || l.startsWith("9)")) ? l.substring(2).trim() : l;
            l = (l.startsWith("(1)") || l.startsWith("(2)") || l.startsWith("(3)") || l.startsWith("(4)") || l.startsWith("(5)") || l.startsWith("(6)") || l.startsWith("(7)") || l.startsWith("(8)") || l.startsWith("(9)")) ? l.substring(3).trim() : l;
            l = l.toLowerCase().trim();
            l = l.endsWith(":") ? l.substring(0, l.length() - ":".length()).trim() : l;
            l = l.endsWith(".") ? l.substring(0, l.length() - ".".length()).trim() : l;
            if (backgroundSet.contains(l)) {
                backgroundList.add(trans);
            }
            if (discussionSet.contains(l)) {
                discussionList.add(trans);
            }
            if (methodsSet.contains(l)) {
                methodsList.add(trans);
            }
            if (resultsSet.contains(l)) {
                resultsList.add(trans);
            }
        }
        En.AbKeyMethod abKeyMethod = new En.AbKeyMethod();
        abKeyMethod.setBackground(JSONUtil.toJsonStr(backgroundList));
        abKeyMethod.setDiscussion(JSONUtil.toJsonStr(discussionList));
        abKeyMethod.setMethods(JSONUtil.toJsonStr(methodsList));
        abKeyMethod.setResults(JSONUtil.toJsonStr(resultsList));
        return abKeyMethod;
    }

    public static List<En.Trans> transAbstract(String ab, List<String> originalWordList, Set<String> smallWordSet) {
        List<En.Trans> transList = new ArrayList<>();
        if (null != ab && ab.length() > 3) {
            String[] split = ab.trim().split("</p>");
            for (String s : split) {
                s = s.startsWith("<p>") ? s.substring("<p>".length()) : s;
                if (s.contains("<strong class=")) {
                    if (s.contains("<strong class=\"sub-title\">") && s.contains("</strong>")) {
                        s = s.replace("<strong class=\"sub-title\">", " ").replace("</strong>", "âãä").replaceAll(" +", " ").trim();
                    }
                    transList.add(addTrans(s));
                } else {
                    String t = ". " + s.replace("：", ":").replace("。", ". ").replace("（", "(").replace("）", ")").replaceAll(" +", " ").trim();
                    t = addLabel(t, originalWordList, smallWordSet);
                    String[] strings = t.split("ôõö");
                    if (strings.length <= 2) {
                        transList.add(addTrans(t.replace("ôõö", "").replace("âãä", "")));
                    } else {
                        for (String string : strings) {
                            if (string.replace(".", "").trim().length() > 1) {
                                transList.add(addTrans(string));
                            }
                        }
                    }
                }
            }
        }
        return transList;
    }

    // originalWordList是从长到短的，匹配上最长的，即停止。simpWordSet是出现次数在一万条以上的，仅127条，全部为小写。
    public static String addLabel(String str, List<String> originalWordList, Set<String> smallWordSet) {
        String[] split = str.split("\\.");
        StringBuilder r = new StringBuilder();
        boolean isColon = isColon(str, smallWordSet);
        boolean isCapital = isCapital(str, smallWordSet);
        boolean isBlank = isBlank(str, smallWordSet);
        boolean isBracket = isBracket(str, smallWordSet);
        for (String s : split) {
            String f = s.startsWith(" ") ? " " : ""; // 如果有空格，那么，仍然保留。
            s = s.trim();
            boolean notFind = true; // 如果没有匹配上，则直接添加到结果中
            if (!s.isEmpty()) {
                for (String o : originalWordList) {
                    o = o.toLowerCase();
                    if (isBracket) { // 1) PURPOSE: To demonstrate 针对有序号的
                        for (int i = 1; i < 10; i++) {
                            if (s.toLowerCase().startsWith(i + ") " + o + ": ")) {
                                int len = o.length() + ") ".length() + Integer.toString(i).length();
                                r.append(".").append(f).append("ôõö").append(s, 0, len).append("âãä").append(s.substring(len));
                                notFind = false;
                                break;
                            } else if (s.toLowerCase().startsWith(i + ")" + o + ": ")) {
                                int len = o.length() + ")".length() + Integer.toString(i).length();
                                r.append(".").append(f).append("ôõö").append(s, 0, len).append("âãä").append(s.substring(len));
                                notFind = false;
                                break;
                            } else if (s.toLowerCase().startsWith("(" + i + ") " + o + ": ")) {
                                int len = o.length() + ("(" + i + ") ").length();
                                r.append(".").append(f).append("ôõö").append(s, 0, len).append("âãä").append(s.substring(len));
                                notFind = false;
                                break;
                            } else if (s.toLowerCase().startsWith("(" + i + ")" + o + ": ")) {
                                int len = o.length() + ("(" + i + ")").length();
                                r.append(".").append(f).append("ôõö").append(s, 0, len).append("âãä").append(s.substring(len));
                                notFind = false;
                                break;
                            }
                        }
                    } else if (s.toLowerCase().startsWith(o) && s.length() > o.length() + 1) {
                        String e1 = s.substring(o.length(), o.length() + 1);
                        String e2 = s.substring(o.length() + 1, o.length() + 2);
                        if (isColon) { // 如果最常用的一些标签后面含有冒号加空格，那么就认定本摘要是这种格式的，就不认冒号后面没有空格的为标签了。此方法仅127个出现一万次以上的标签，会漏掉一些，影响不大。
                            if (":".equals(e1) && " ".equals(e2)) { // 标签后的那个字符，如果是大写字母或冒号或句号，则认为是标签，如果是标签，就要加冒号
                                r.append(".").append(f).append("ôõö").append(s, 0, o.length()).append("âãä").append(s.substring(o.length()));
                                notFind = false;
                                break;
                            }
                        } else if (isCapital) { // 如果是标签后面紧跟着大写字母或数字，而不是冒号、句号或空格，这种仅限于出现次数在一万条以上的标签。
                            if (smallWordSet.contains(o) && Pattern.compile("[A-Z0-9]").matcher(e1).find()) { // 标签后的那个字符，如果是大写字母或冒号或句号，则认为是标签，如果是标签，就要加冒号
                                r.append(".").append(f).append("ôõö").append(s, 0, o.length()).append("âãä").append(s.substring(o.length()));
                                notFind = false;
                                break;
                            }
                        } else if (isBlank) { // 这种是没有冒号，而是标签后面有一个空格，空格后面必须又是一个大写的字母，但仅限于出现次数在一万条以上的标签。
                            if (smallWordSet.contains(o) && " ".equals(e1) && Pattern.compile("[A-Z]").matcher(e2).find()) { // 标签后的那个字符，如果是大写字母或冒号或句号或空格，则认为是标签，如果是标签，就要加冒号
                                r.append(".").append(f).append("ôõö").append(s, 0, o.length()).append("âãä").append(s.substring(o.length()));
                                notFind = false;
                                break;
                            }
                        } else { // 其它的，都走这种方法了，即标签后可以是大写字母，也可以是冒号，也可以是句号，但不能是空格。
                            if (Pattern.compile("[A-Z:.]").matcher(e1).find()) { // 标签后的那个字符，如果是大写字母或冒号或句号，则认为是标签，如果是标签，就要加冒号
                                r.append(".").append(f).append("ôõö").append(s, 0, o.length()).append("âãä").append(s.substring(o.length()));
                                notFind = false;
                                break;
                            }
                        }
                    }
                }
            }
            if (notFind) {
                r.append(".").append(f).append(s);
            }
        }
        return r.toString();
    }

    // 这种是标签后面冒号加空格型的
    public static boolean isColon(String str, Set<String> smallWordSet) {
        String n = str.replace(". ", ".").toLowerCase();
        for (String s : smallWordSet) {
            if (n.contains("." + s.toLowerCase() + ": ")) {
                return true;
            }
        }
        return false;
    }

    // 1) PURPOSE: To demonstrate 针对有序号的
    public static boolean isBracket(String str, Set<String> smallWordSet) {
        String n = str.replace("（", "(").replace(". ", ".").replace("）", ")").toLowerCase();
        int sum = 0;
        for (String s : smallWordSet) {
            for (int i = 1; i < 10; i++) {
                if (n.contains("." + i + ") " + s.toLowerCase() + ": ") || n.contains(".(" + i + ") " + s.toLowerCase() + ": ")) {
                    sum++;
                }
            }
        }
        return sum > 1;
    }

    // 这种是没有冒号，而是标签后面有一个空格，空格后面必须又是一个大写的字母，但仅限于出现次数在一万条以上的标签。
    public static boolean isBlank(String str, Set<String> smallWordSet) {
        String[] split = str.split("\\.");
        int sum = 0;
        for (String s : split) {
            s = s.trim();
            for (String o : smallWordSet) {
                if (s.toLowerCase().startsWith(o.toLowerCase()) && s.length() > o.length() + 1 && o.trim().length() > 2) {
                    String e1 = s.substring(o.length(), o.length() + 1);
                    String e2 = s.substring(o.length() + 1, o.length() + 2);
                    if (" ".equals(e1) && Pattern.compile("[A-Z]").matcher(e2).find()) {
                        sum++;
                    }
                }
            }
        }
        return sum > 1;
    }

    // 如果是标签后面紧跟着大写字母，而不是冒号、句号或空格，这种仅限于出现次数在一万条以上的标签。
    public static boolean isCapital(String str, Set<String> smallWordSet) {
        String[] split = str.split("\\.");
        int sum = 0;
        for (String s : split) {
            s = s.trim();
            for (String o : smallWordSet) {
                if (s.toLowerCase().startsWith(o.toLowerCase()) && s.length() > o.length() && o.trim().length() > 2) {
                    String e = s.substring(o.length(), o.length() + 1);
                    if (Pattern.compile("[A-Z]").matcher(e).find()) { // 标签后的那个字符，如果是大写字母，则认为是标签，如果是标签，就要加冒号
                        sum++;
                    }
                }
            }
        }
        return sum > 1;
    }

    public static En.Trans addTrans(String str) {
        En.Trans trans = new En.Trans();
        for (int i = 0; i < str.length(); i++) {
            str = str.trim();
            if (str.startsWith(".")) {
                str = str.substring(".".length()).trim();
            } else if (str.endsWith(".")) {
                str = str.substring(0, str.length() - ".".length()).trim();
            } else {
                break;
            }
        }
        str = !str.endsWith(".") ? str + ". " : str;
        if (!Utils.isContainChinese(str)) {
            if (str.contains("âãä") && str.length() > 5) {
                String label = str.substring(0, str.indexOf("âãä")).trim();
                label = label.endsWith(":") ? label.substring(0, label.length() - ":".length()) : label;
                String en = str.substring(str.indexOf("âãä") + "âãä".length()).trim();
                en = en.startsWith(":") ? en.substring(":".length()).trim() : en;
                trans.setLabel(label);
                trans.setEn(en);
//                trans.setCn(StudyUtils.enToCnTrans((label + ": " + en)).replace("::", ":"));
            } else if (str.trim().length() > 1) {
                trans.setEn(str);
//                trans.setCn(StudyUtils.enToCnTrans(str));
            }
        }
        return trans;
    }

    public static void addKeySet(String keyword, Set<List<String>> keySet, Map<List<String>, Integer> keyWordSumMap, Map<String, Integer> cnKeyWordSumMap) {
        if (null != keyword && keyword.length() > 2) {
            String[] split = keyword.replace("（", "(").replace("）", ")").replace("；", "; ").replace("，", ",").replace("。", "; ")
                    .replaceAll(" +", " ").replace("( ", "(").replace(" )", ")").replace(" ,", ",").replace(", ", ",").split("; ");
            for (String keyWord : split) {
                boolean isChinese = Utils.isContainChinese(keyWord);
                if (isChinese) {
                    if (keyWord.length() > 1) {
                        // 内(Fracturefixation,internal)骨折固定术->内骨折固定术;(Fracturefixation,internal)
                        String str = keyWord.replaceAll("(.*)(\\(.*\\))(.*)", "$1$3ĺļ$2");
                        if (str.contains("ĺļ")) {
                            String[] strSplit = str.split("ĺļ");
                            if (split.length == 2) {
                                String s = strSplit[1].trim();
                                s = s.startsWith("(") && s.endsWith(")") ? s.substring("(".length(), s.length() - ")".length()) : s;
                                // 如果括号内是中文，忽略括号。如果是英文，再检查是全写，还是简写，如果符合要求，加入。
                                if (Utils.isContainChinese(s)) {
                                    cnKeyWordSumMap.compute(str, (key, value) -> value != null ? (value + 1) : 1);
                                } else {
                                    cnKeyWordSumMap.compute(strSplit[0], (key, value) -> value != null ? (value + 1) : 1);
                                    putKeyWordSumMap(s, keySet, keyWordSumMap);
                                }
                            } else {
                                cnKeyWordSumMap.compute(str.replace("ĺļ", ""), (key, value) -> value != null ? (value + 1) : 1);
                            }
                        } else {
                            cnKeyWordSumMap.compute(keyWord.trim(), (key, value) -> value != null ? (value + 1) : 1);
                        }
                    }
                } else {
                    putKeyWordSumMap(keyWord, keySet, keyWordSumMap);
                }
            }
        }
    }

    public static void putKeyWordSumMap(String keyWord, Set<List<String>> keySet, Map<List<String>, Integer> keyWordSumMap) {
        if (isKeyWordFull(keyWord) || isKeyWordAbbr(keyWord)) {
            keyWord = keyWord.trim();
            keyWord = keyWord.endsWith(".") ? keyWord.substring(0, keyWord.length() - 1) : keyWord;
            List<String> fullAbbrList = new ArrayList<>();
            // 全部是大写，或者不包含空格，首字母之后含有大写，可以认定为缩写词，缩写词是不改变大小写的。
            if (!Pattern.compile("[a-z]").matcher(keyWord).find() || (!keyWord.contains(" ") && Pattern.compile("[A-Z]").matcher(keyWord.substring(1)).find())) {
                fullAbbrList.add(keyWord.trim());
            } else {
                fullAbbrList.add(keyWord.toLowerCase().trim());
            }
            keySet.add(fullAbbrList);
            keyWordSumMap.compute(fullAbbrList, (key, value) -> value != null ? (value + 1) : 1);
        }
    }

    // 全写的关键词的认定标准，要求至少6位，且少于65位，且以空格切割小于10个单词。anaplastic lymphoma kinase-positive nonsmall cell lung cancer 这个字符串为61位
    public static boolean isKeyWordFull(String full) {
        return null != full && full.length() > 5 && full.length() < 65 && full.replace("-", " ").split(" ").length < 10;
    }

    // 如果缩写的单词为3位，要求必须全部是大写，且不含数字和空格，如果是4位或以上，则要求除首位外其它必须得有一个大写字母或包含"-"才可以。缩写的区分大小写，即6位以下的。
    public static boolean isKeyWordAbbr(String abbr) {
        if (null == abbr || abbr.length() < 3) {
            return false;
        }
        if (abbr.length() < 4) {
            return !Pattern.compile("[a-z0-9 ]").matcher(abbr).find();
        } else {
            return (Pattern.compile("[A-Z]").matcher(abbr.substring(1)).find() || abbr.contains("-"));
        }
    }


}
